package com.hs.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.hs.gateway.properties.NotAuthUrlProperties;
import com.hs.gateway.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.security.PublicKey;
import java.util.Map;

/**
 * @Description: TODO
 * @Author 胡尚
 * @Date: 2024/7/26 16:41
 */
@Slf4j
@Component
@Order(1)
@EnableConfigurationProperties(value = NotAuthUrlProperties.class)
public class AuthenticationFilter implements GlobalFilter, InitializingBean {

    @Autowired
    private NotAuthUrlProperties notAuthUrlProperties;

    @Autowired
    private RestTemplate restTemplate;

    private PublicKey publicKey;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化bean过程中向授权服务器发送请求，获取公钥
        publicKey = JwtUtils.genPulicKey(restTemplate);
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        String path = exchange.getRequest().getURI().getPath();

        // 过滤不需要认证的url
        if (shouldSkip(path)) {
            log.info("请求不用认证：{}", path);
            return chain.filter(exchange);
        }
        log.info("对请求进行校验：{}", path);

        // 获取token
        // 解析出我们Authorization的请求头  value为: “bearer XXXXXXXXXXXXXX”
        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StringUtils.isEmpty(authHeader)) {
            log.warn("不是放行请求，却未携带token:{}", path);
            // TODO 抛业务自定义异常 我这里就直接随便抛异常了
            throw new RuntimeException();
        }


        //3. 校验token
        // 拿到token后，通过公钥（需要从授权服务获取公钥）校验
        // 校验失败或超时抛出异常
        //第三步 校验我们的jwt 若jwt不对或者超时都会抛出异常
        Claims claims = JwtUtils.validateJwtToken(authHeader, publicKey);

        //4. 校验通过后，从token中获取的用户登录信息存储到请求头中
        //第四步 把从jwt中解析出来的 用户登陆信息存储到请求头中
        ServerWebExchange webExchange = wrapHeader(exchange,claims);

        return chain.filter(webExchange);
    }

    /**
     * 过滤掉不需要认证的url
     *
     * @param requestPath 当前请求
     * @return true表示不需要认证
     */
    private boolean shouldSkip(String requestPath) {
        //路径匹配器(简介SpringMvc拦截器的匹配器)
        //比如/oauth/** 可以匹配/oauth/token    /oauth/check_token等
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        for (String shouldSkipUrl : notAuthUrlProperties.getShouldSkipUrls()) {
            if (antPathMatcher.match(shouldSkipUrl, requestPath)) {
                return true;
            }
        }
        return false;
    }


    private ServerWebExchange wrapHeader(ServerWebExchange serverWebExchange,Claims claims) {

        String loginUserInfo = JSON.toJSONString(claims);

        log.info("jwt的用户信息:{}",loginUserInfo);

        Map<String, Object> additionalInfo = claims.get("additionalInfo", Map.class);
        Integer userId = (Integer) additionalInfo.get("userId");
        String userName = (String) additionalInfo.get("userName");


        //向headers中放文件，记得build
        ServerHttpRequest request = serverWebExchange.getRequest().mutate()
                .header("username",userName)
                .header("userId",userId+"")
                .build();

        //将现在的request 变成 change对象
        return serverWebExchange.mutate().request(request).build();
    }
}
